<!DOCTYPE html>
<html lang="en">
  
  <head>
    <meta charset="UTF-8">
    <title>Expand Nodes with Force Layout</title>
  </head>
  
  <body>
    <div id="mountNode"></div>
    <script src="../build/g6.js"></script>
    <script src="../build/force.js"></script>
    <script src="./assets/d3-4.13.0.min.js"></script>
    <script>
      const getNodeById = {
        "success": true,
        "data": {
          "nodes": [{
            "id": "20",
            "nodeType": "Person",
            "label": "name1",
            "properties": {
              "age": "37",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "M",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "name1",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "1",
            "nodeType": "Person",
            "label": "name2",
            "properties": {
              "age": "31",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "FM",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "name2",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          }],
          "edges": [{
            "source": "20",
            "target": "1"
          }],
          "queryResult": "",
          "graphView": true
        },
        "errorCode": null,
        "msg": null
      }

      // 根据节点扩展
      const getNodesByNodeId = {
        "success": true,
        "data": {
          "nodes": [{
            "id": "20",
            "nodeType": "Person",
            "label": "祁同伟",
            "properties": {
              "age": "37",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "M",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "祁同伟",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "22",
            "nodeType": "Person",
            "label": "高晓秦",
            "properties": {
              "age": "23",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "高晓秦",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "10",
            "nodeType": "Person",
            "label": "高育良",
            "properties": {
              "age": "57",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "M",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "高育良",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "12",
            "nodeType": "Person",
            "label": "高晓凤",
            "properties": {
              "age": "22",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "高晓凤",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "21",
            "nodeType": "Person",
            "label": "梁璐",
            "properties": {
              "age": "40",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "梁璐",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "11",
            "nodeType": "Person",
            "label": "吴淑芬",
            "properties": {
              "age": "50",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "吴淑芬",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "100",
            "nodeType": "Enterprise",
            "label": "小米",
            "properties": {
              "IsChineseFunded": "",
              "businessScope": "",
              "credit": "",
              "employeesNumber": "0",
              "enterpriseNature": "",
              "establishmentTime": "",
              "holdingType": "",
              "importExportRightMark": "",
              "industryType": "",
              "isAdvantageEnterprise": "",
              "isLeadingEnterprise": "",
              "isOnBlacklist": "",
              "isOvercapacity": "",
              "isRegionalKeyEnterprise": "",
              "isSangaoEnterprise": "",
              "name": "小米",
              "registeredCapital": "0",
              "registrationAddress": "",
              "sbh": "",
              "size": "",
              "stockCode": "",
              "telephone": ""
            }
          },
          {
            "id": "102",
            "nodeType": "Enterprise",
            "label": "茅台",
            "properties": {
              "IsChineseFunded": "",
              "businessScope": "",
              "credit": "",
              "employeesNumber": "0",
              "enterpriseNature": "",
              "establishmentTime": "",
              "holdingType": "",
              "importExportRightMark": "",
              "industryType": "",
              "isAdvantageEnterprise": "",
              "isLeadingEnterprise": "",
              "isOnBlacklist": "",
              "isOvercapacity": "",
              "isRegionalKeyEnterprise": "",
              "isSangaoEnterprise": "",
              "name": "茅台",
              "registeredCapital": "0",
              "registrationAddress": "",
              "sbh": "",
              "size": "",
              "stockCode": "",
              "telephone": ""
            }
          }],
          "edges": [{
            "target": "100",
            "source": "20"
          },
          {
            "target": "102",
            "source": "20"
          },
          {
            "target": "10",
            "source": "20"
          },
          {
            "target": "11",
            "source": "20"
          },
          {
            "target": "21",
            "source": "20"
          },
          {
            "target": "21",
            "source": "20"
          },
          {
            "target": "22",
            "source": "20"
          },
          {
            "target": "11",
            "source": "10",
          },
          {
            "target": "12",
            "source": "10"
          },
          {
            "target": "22",
            "source": "21",
          },
          {
            "target": "22",
            "source": "12"
          }],
          "queryResult": "",
          "graphView": true
        },
        "errorCode": null,
        "msg": null
      }

      // 根据节点扩展
      const getNodesByNodeId2 = {
        "success": true,
        "data": {
          "nodes": [{
            "id": "22",
            "nodeType": "Person",
            "label": "高晓秦",
            "properties": {
              "age": "23",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "高晓秦",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "1000",
            "nodeType": "Person",
            "label": "晓秦1",
            "properties": {
              "age": "57",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "M",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "晓秦1",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "1001",
            "nodeType": "Person",
            "label": "晓秦2",
            "properties": {
              "age": "22",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "晓秦2",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "1002",
            "nodeType": "Person",
            "label": "晓秦3",
            "properties": {
              "age": "40",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "晓秦3",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "1003",
            "nodeType": "Person",
            "label": "晓秦4",
            "properties": {
              "age": "40",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "晓秦4",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          },
          {
            "id": "1004",
            "nodeType": "Person",
            "label": "晓秦5",
            "properties": {
              "age": "40",
              "annualIncome": "0",
              "career": "",
              "credit": "",
              "education": "",
              "email": "",
              "gender": "F",
              "highestEducation": "",
              "isBusiness": "",
              "isOnBlacklist": "",
              "name": "晓秦5",
              "permanentResidenceAddress": "",
              "residentialAddress": "",
              "sbh": "",
              "telephone": "",
              "workUnit": ""
            }
          }],
          "edges": [{
            "target": "22",
            "source": "1000"
          },
          {
            "target": "22",
            "source": "1001"
          },
          {
            "target": "22",
            "source": "1002"
          },
          {
            "target": "1001",
            "source": "1002"
          },
          {
            "target": "1000",
            "source": "1"
          },
          {
            "target": "20",
            "source": "1002"
          },
          {
            "target": "22",
            "source": "1003"
          },
          {
            "target": "22",
            "source": "1004"
          }],
          "queryResult": "",
          "graphView": true
        },
        "errorCode": null,
        "msg": null
      }

      let clickedNodeId = -1;
      let clickedNodePos = {
        'x': 500,
        'y': 300
      };

      // 封装点击添加边的交互
      G6.registerBehavior('click-add-node', {
        getEvents() {
          return {
            'node:click': 'onClick'
          };
        },
        onClick(ev) {
          const itemModel = ev.item.getModel();
          clickedNodeId = itemModel.id;
          clickedNodePos.x = itemModel.x;
          clickedNodePos.y = itemModel.y;
          const graph = this.graph;
          const nodes = graph.getNodes();
          const edges = graph.getEdges();
          let newData = getNodeById.data;
          if (itemModel.id == 20) newData = getNodesByNodeId.data;
          else if (itemModel.id == 22) newData = getNodesByNodeId2.data;
          let newNodeModels = newData.nodes;
          let newEdgeModels = newData.edges;
          let allNodeModels = [],
          allEdgeModels = [];

          // fix the existing nodes by set the fx and fy to the current value
          // to cancel the fix the node, set fx = null fy = null
          nodes.forEach((n, i) => {
            const model = n.getModel();
            model.fixed = true;
            model.fx = model.x;
            model.fy = model.y;
            allNodeModels.push(model);
          });

          newNodeModels.forEach((nodeModel, i) => {
            // if it does not exist in nodes
            let exist = false;
            nodes.forEach((n, i) => {
              if (n.getModel().id == nodeModel.id) {
                exist = true;
                return;
              }
            });
            // then add it into graph
            if (!exist) {
              // set the initial positions of the new nodes at the clicked node
              nodeModel.x = itemModel.x;
              nodeModel.y = itemModel.y;
              const node = graph.addItem('node', nodeModel);
              allNodeModels.push(nodeModel);
              nodes.push(node);
            }
          });

          newEdgeModels.forEach((em, i) => {
            // if it does not exist in nodes
            let exist = false;
            edges.forEach((e, i) => {
              const eModel = e.getModel();
              if (eModel.source == em.source && eModel.target == em.target) {
                exist = true;
                return;
              }
            });
            if (!exist) {
              const edge = graph.addItem('edge', em);
              edges.push(edge);
            }
          });
          edges.forEach((e, i) => {
            allEdgeModels.push(e.getModel());
          });

          graph.changeData({
            nodes: allNodeModels,
            edges: allEdgeModels
          });
        }
      });

      let data = getNodeById.data;

      const graph = new G6.Graph({
        container: 'mountNode',
        width: 1000,
        height: 600,
        layout: {
          type: 'force',
          center: [500, 300],
          // 向心力作用点
          nodeStrength: -30,
          // 节点作用力
          preventOverlap: true,
          // 是否防止节点相互覆盖
          nodeSize: 40,
          // 节点大小 / 直径
          edgeStrength: 0.1,
          // 边的作用力, 默认为根据节点的入度出度自适应
          linkDistance: 100,
          // 默认边长度
        },
        modes: {
          default: ['drag-node', 'click-select', 'click-add-node']
        },
        defaultNode: {
          size: [40, 40],
          color: 'steelblue'
        },
        defaultEdge: {
          size: 1,
          color: '#e2e2e2'
        }
      });

      graph.data(data);

      graph.render();
      graph.on('node:dragend', ev => {
        const itemModel = ev.item.getModel();
        itemModel.fixed = true;
        itemModel.fx = ev.x;
        itemModel.fy = ev.y;
      });
    </script>
  </body>

</html>